using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

using Mapack;

namespace A4G.Core
{
	// [Serializable]
	public class StateMachine<TState, TEvent> : StateMachineEntity<TState, TEvent>
	{
		public StateMachine(TState[] states, TEvent[] events, TState initialState)
		{
			base.Method = new StateMachineDelegate<TState, TEvent>(ExecuteMethod);

			_states = states;
			_events = events;

			CurrentState = initialState;

			_entities = new StateMachineEntity<TState, TEvent>[Events.Length][];
			for (int i = 0; i < Events.Length; i++)
			{
				_entities[i] = new StateMachineEntity<TState, TEvent>[States.Length];
			}
		}

		private TState _currentState;
		public TState CurrentState
		{
			get { return _currentState; }
			set { _currentState = value; }
		}

		private readonly TState[] _states;
		public TState[] States
		{
			get { return _states; }
		}

		private readonly TEvent[] _events;
		public TEvent[] Events
		{
			get { return _events; }
		}

		private StateMachineEntity<TState,TEvent>[][] _entities;
		private StateMachineEntity<TState, TEvent>[][] Entities
		{
			get { return _entities; }
		}

		private bool _aborted;
		public bool Aborted
		{
			get { return _aborted; }
			set { _aborted = value; }
		}

		public void Abort()
		{
			Aborted = true;
		}

		public StateMachineEntity<TState, TEvent> this[TState s, TEvent e]
		{
			set
			{
				int i = GetIndexOf<TState>(States, s);
				int j = GetIndexOf<TEvent>(Events, e);
				Entities[i][j] = value;
			}
			get
			{
				int i = GetIndexOf<TState>(States, s);
				int j = GetIndexOf<TEvent>(Events, e);
				return Entities[i][j];
			}
		}

		public StateMachineEntity<TState, TEvent> this[int i, int j]
		{
			set
			{
				Entities[i][j] = value;
			}
			get
			{
				return Entities[i][j];
			}
		}

		public int GetIndexOf<T>(T[] array, T t)
		{
			int i = 0;
			foreach (T ti in array)
			{
				if (t.Equals(ti))
				{
					return i;
				}
				i++;
			}


			throw new ApplicationException("Entity not found.");
		}

		public override bool ExecuteMethod(TEvent e, ref TState outState)
		{
			bool terminated = false;
			while ((!terminated) && (!Aborted))
			{
				TState state = default(TState);
				terminated = this[CurrentState, e].ExecuteMethod(e, ref state);
				CurrentState = state;
			}
			return terminated;
		}

		public bool ExecuteAsync(TEvent e, ref TState outState)
		{
			// Todo: make multithreaded
			return ExecuteMethod(e, ref outState);
		}
	}
}
